import * as effectTypes from './effectTypes';
import { TASK_CANCEL } from './symbols';

/**
 * 执行或者启动saga的方法，类似于co库
 * @param {*} saga 
 */
function runSaga(env, saga, doneCallback) {
    let task = { cancel: () => next(TASK_CANCEL) }
    const { getState, dispatch, channel } = env;
    // saga可能是一个生成器，也可能是一个迭代器
    let it = typeof saga === 'function' ? saga() : saga;

    function next(value, isError) {
        let result;
        if (isError) {
            result = it.throw(value);
        } else if (value === TASK_CANCEL) {
            result = it.return(value);
        } else {
            result = it.next(value);
        }
        let { value: effect, done } = result;
        // {type: "TAKE", actionType: "ASYNC_ADD"}
        // {type: "PUT", action: {type: "ADD"}}
        // {type: "ALL", effects: [ADD1, ADD2]}
        console.log(effect);
        if (!done) {
            // effect是一个迭代器
            if (typeof effect[Symbol.iterator] === 'function') {
                runSaga(env, effect);
                next();
            // effect是一个promise
            } else if (typeof effect.then === 'function') {
                effect.then(next);
            } else {
                switch (effect.type) {
                    case effectTypes.TAKE:
                        channel.take(effect.actionType, next); // 订阅next函数,停止向下执行，等到dispatch(action),发布next()函数，后，继续往下执行
                        break; // 没有 next()，暂停执行
                    case effectTypes.PUT:
                        dispatch(effect.action); // 异步action在这里触发，触发同步的action  ASYNC_ADD => ADD
                        next(); // 派发完后立刻向下执行
                        break;
                    case effectTypes.FORK: // 开启一个新的子进程去运行saga
                        const forkTask = runSaga(env, effect.saga);
                        next(forkTask);
                        break;
                    case effectTypes.CALL: // 调用函数，并传参
                        effect.fn(...effect.args).then(next);
                        break;
                    case effectTypes.CPS: // 调用函数，并传参，自己构建callback
                        effect.fn(...effect.args, (err, data) => {
                            if (err) {
                                next(err, true);
                            } else {
                                next(data);
                            }
                        });
                        break;
                    case effectTypes.ALL:
                        let effects = effect.effects;
                        let result = [];
                        let completedCount = 0;
                        effects.forEach((effect, index) => {
                            // 所有saga都执行完毕后next()
                            runSaga(env, effect, (res) => {
                                result[index] = res;
                                if (++completedCount === effects.length) {
                                    next(result);
                                }
                            })
                        })
                        break;
                    case effectTypes.CANCEL:
                        effect.task.cancel();
                        next();
                        break;
                    case effectTypes.SELECT:
                        let state = effect.selector(getState());
                        next(state);
                        break;
                    default:
                        break;
                }
            }
        } else {
            // done为ture，整个saga结束了，调用结束回调
            doneCallback && doneCallback(result.value);
        }
    }
    next();
    return task;
}

export default runSaga;
